#include "i2cemu.h"
#include "soc/soc_misc.h"
#include "soc/soc_gpio.h"


#define IIC_DELAY()  SOC_Misc::DelayUs(m_clkDelay)
#define IIC_SCL(v)   SOC_Gpio::Write(m_SCLPortPin, v)
#define IIC_SDA(v)   SOC_Gpio::Write(m_SDAPortPin, v)
#define IIC_READ_SDA()  SOC_Gpio::Read(m_SDAPortPin)

#define IIC_WAIT_ACK_TIMEOUT_CNT  250

I2CEmu::I2CEmu()
{
}

I2CEmu::I2CEmu(const GpioPortPin &sclPortPin, const GpioPortPin &sdaPortPin)
{
    Config(sclPortPin, sdaPortPin);
}

I2CEmu::~I2CEmu()
{
}

void I2CEmu::Config(const GpioPortPin &sclPortPin, const GpioPortPin &sdaPortPin)
{
    m_SCLPortPin = sclPortPin;
    m_SDAPortPin = sdaPortPin;
}

void I2CEmu::SetClkDelay(int us)
{
    m_clkDelay = us;
}

bool I2CEmu::SendByte(uint8_t dat)
{
    uint8_t t;
    
    for (t=0; t<8; t++)
    {
        IIC_SDA(((dat & 0x80) >> 7));
        IIC_DELAY();
        IIC_SCL(1);
        IIC_DELAY();
        IIC_SCL(0);
        dat <<= 1;
    }
    IIC_SDA(1);
		
	return true;
}

uint8_t I2CEmu::ReadByte(bool ack)
{
    uint8_t i;
    uint8_t dat = 0;
    
    for (i = 0; i < 8; i++ )
    {
        dat <<= 1;
        IIC_SCL(1);
        IIC_DELAY();
        
        if (IIC_READ_SDA())
        {
            dat++;
        }
        
        IIC_SCL(0);
        IIC_DELAY();
    }
    
    if (ack == 0)
    {
        NAckSignal();
    }
    else
    {
        AckSignal();
    }

    return dat;
}

void I2CEmu::StartSignal()
{
    IIC_SDA(1);
    IIC_SCL(1);
    IIC_DELAY();
    IIC_SDA(0);
    IIC_DELAY();
    IIC_SCL(0);
    IIC_DELAY();
}

void I2CEmu::StopSignal()
{
    IIC_SDA(0);
    IIC_DELAY();
    IIC_SCL(1);
    IIC_DELAY();
    IIC_SDA(1);
    IIC_DELAY();
}

bool I2CEmu::WaitAckSignal()
{
    uint8_t waittime = 0;
    uint8_t rack = 0;
    
    IIC_SDA(1);
    IIC_DELAY();
    IIC_SCL(1);
    IIC_DELAY();
    
    while (IIC_READ_SDA())
    {
        waittime++;
        
        if (waittime > IIC_WAIT_ACK_TIMEOUT_CNT)
        {
            StopSignal();
            rack = 1;
            break;
        }
    }
    
    IIC_SCL(0);
    IIC_DELAY();
    
    return rack;
}

void I2CEmu::AckSignal()
{
    IIC_SDA(0);
    IIC_DELAY();
    IIC_SCL(1);
    IIC_DELAY();
    IIC_SCL(0);
    IIC_DELAY();
    IIC_SDA(1);
    IIC_DELAY();
}

void I2CEmu::NAckSignal()
{
    IIC_SDA(1);
    IIC_DELAY();
    IIC_SCL(1);
    IIC_DELAY();
    IIC_SCL(0);
    IIC_DELAY();
}
